/**
 * Copyright (c) 2021 EdgerOS Team.
 * All rights reserved.
 * 
 * Detailed license information can be found in the LICENSE file.
 * 
 * Author       : Fu Wenhao <fuwenhao@acoinfo.com>
 * Date         : 2022-02-10 09:57:31
 * LastEditors  : Fu Wenhao <fuwenhao@acoinfo.com>
 * LastEditTime : 2022-02-17 16:55:12
 */
// import EventEmitter from 'events';
// import dgram from 'dgram';
// import * as os from 'os';
// import * as net from "net";

const EventEmitter = require('events');
const dgram = require('dgram');
const os = require('os');
const net = require('net')

//EdgerOS GETWAY IP
const edgerosGetWayIp = '192.168.128'

// TYPE
const SDDC_TYPE_DISCOVER = 0x00
const SDDC_TYPE_REPORT = 0x01
const SDDC_TYPE_UPDATE = 0x02
const SDDC_TYPE_INVITE = 0x03
const SDDC_TYPE_PING = 0x04
const SDDC_TYPE_MESSAGE = 0x05

// Header flages
const SDDC_FLAG_NONE = 0x00
const SDDC_FLAG_ACK = 0x80
const SDDC_FLAG_REQ = 0x40
const SDDC_FLAG_JOIN = 0x20
const SDDC_FLAG_URGENT = 0x10



/**
 * SDDC 对象
 */
class SddcInstance extends EventEmitter {
  constructor(config) {
    super();
    this.config = config
    this.reportData = null
    this.inviteData = null
    this.uid = ''
    this.server = null
    this.ackNum = 0
    this.edgerInfo = null //所加入的网关信息
    this.edgerosIP = config.edgerosIP || '192.168.128.1'
    this.edgerosPort = config.edgerosPort || 680
    this.connector = null
    this.init();
  }

  /**
   * 启动初始化SDDC
   */
  init() {
    this.setUid()
    this.setReportData(this.config)
    this.setInviteData(this.config)
    this.server = dgram.createSocket('udp4');
    this.server.on('error', (err) => {
      console.log(`Server error:\n${err.stack}`);
      server.close();
    });
    this.server.on('listening', () => {
      const address = this.server.address();
      console.log(`UDP server listening ${address.address}:${address.port}`);
    });
    this.server.on('message', (msg, rinfo) => {
      // console.log(`server got: ${msg.toString('hex')} from ${rinfo.address}:${rinfo.port}`);
      this.sddcReadHandle(msg, rinfo)
    })
    this.server.bind(this.edgerosPort);
  }

  /**
   * 设置report 数据
   * @param {*} msg 
   * @param {*} remoteInfo 
   */
  setReportData(config) {
    this.reportData = {
      report: {
        name: config.name || 'PC-Node',
        type: config.type || 'device',// "monitor", "edger", "device"
        excl: config.excl || false,// 此设备是否应用独占
        desc: config.desc || 'PC端 EdgerOS 节点',
        model: config.model || '1',
        vendor: config.vendor || 'rMRC',
        sn: this.uid
      }
    }
  }
  /**
   * 设置InviteData
   */
  setInviteData(config) {
    this.inviteData = {
      report: {
        name: config.name || 'PC-Node',
        type: config.type || 'device',// "monitor", "edger", "device"
        excl: config.excl || false,// 此设备是否应用独占
        desc: config.desc || 'PC端 EdgerOS 节点 invite',
        model: config.model || '1',
        vendor: config.vendor || 'iMRC',
        sn: this.uid
      }
    }
  }
  /**
   * 设置UID
   * @param {*} msg 
   * @param {*} remoteInfo 
   */
  setUid() {
    this.uid = '0000000000000001'
    let netWrokData = os.networkInterfaces();
    let mac_ip = null
    for (let netName in netWrokData) {
      for (let ipData of netWrokData[netName]) {
        if (ipData.address.indexOf(edgerosGetWayIp) != -1) {
          mac_ip = ipData.mac
        }
      }
    }
    if (mac_ip) {
      let macArray = mac_ip.split(':')
      macArray.splice(3, 0, '80')
      macArray.splice(3, 0, 'fe')
      this.uid = macArray.join('')
    }
  }

  /**
   * SDDC 接收数据处理
   */
  sddcReadHandle(msg, remoteInfo) {
    const msgObj = {
      magic_ver: msg.slice(0, 1).toString('hex'),
      type: msg.slice(1, 2).readUInt8() & 0x1f,
      flags: msg.slice(1, 2).readUInt8() & 0xe0,
      ack_num: msg.slice(2, 4).readUInt16BE(0),
      uid: msg.slice(4, 12).toString('hex'),
      security: msg.slice(12, 13).toString('hex'),
      Reserved: msg.slice(13, 14).toString('hex'),
      datalength: msg.slice(14, 16).readUint16BE(),
      originalBuffer: msg
    }

    switch (msgObj.type) {
      case SDDC_TYPE_DISCOVER: // 接收到搜索信息
        this.discoverHandle()
        break;
      case SDDC_TYPE_INVITE: // 邀请加入网关
        this.inviteHandle(msgObj, remoteInfo)
        break;
      case SDDC_TYPE_PING:   // PING
        this.pingHandle(msgObj)
        break;
      case SDDC_TYPE_MESSAGE:// Message
        this.messageHandle(msgObj, remoteInfo)
        break;
      default:
        console.log(' sddc 消息不认识的类型:type>:0x' + ('00' + msgObj.type.toString(16)).slice(-2))
    }
  }


  /**
   * 接收消息处理 messageHandle
   * 
   */
  messageHandle(msgObj, remoteInfo) {
    let msgBuf = msgObj.originalBuffer.slice(0 - msgObj.datalength)

    if (msgObj.flags == SDDC_FLAG_ACK) {
      // console.log('接收到 消息 ack 回复消息', msgObj, msgBuf.toString())
    } else {
      // 返回消息
      this.sendToEdgerOS(SDDC_FLAG_ACK | SDDC_TYPE_MESSAGE, null, msgObj.ack_num)
      this.emit("message", msgBuf, remoteInfo)
    }
  }
  /**
   * 发送消息 to EdgerOS
   * str:字符串消息
   */
  sendMessage(str) {
    let type_flags = SDDC_FLAG_NONE | SDDC_TYPE_MESSAGE
    let reqmsgBuf = Buffer.from(str);
    this.sendToEdgerOS(type_flags, reqmsgBuf)
  }
  /**
   * ping 消息处理
   */
  pingHandle(msgObj) {
    let type_flags = SDDC_FLAG_ACK | SDDC_TYPE_PING
    this.sendToEdgerOS(type_flags, null, msgObj.ack_num)
  }


  /**
   * 接收邀请加入网关
   */
  inviteHandle(msgObj, remoteInfo) {
    // 请求邀请信息
    if (msgObj.flags == SDDC_FLAG_REQ) {
      let jsonBuf = msgObj.originalBuffer.slice(0 - msgObj.datalength)
      let inviteStr = JSON.stringify(this.inviteData)
      let inviteBuf = Buffer.from(inviteStr)
      let type_flags = (SDDC_FLAG_ACK | SDDC_FLAG_JOIN) | SDDC_TYPE_INVITE
      this.sendToEdgerOS(type_flags, inviteBuf, msgObj.ack_num)
      this.edgerInfo = JSON.parse(jsonBuf.toString())
      console.log('EdgerOS 邀请加入网络:', this.edgerInfo)
    } else {
      console.log('不认识的 flage 请求', msgObj.flags)
    }
  }

  /**
   * 处理 edgeros 发出的搜索设备指令
   * @param {*} remoteInfo 
   */
  discoverHandle() {
    let reportStr = JSON.stringify(this.reportData)
    let reportBuf = Buffer.from(reportStr)
    let type_flags = SDDC_FLAG_NONE | SDDC_TYPE_REPORT
    this.sendToEdgerOS(type_flags, reportBuf)
  }


  /**
   * 发送 消息给EdgerOS
   * type:消息类型
   * buffer:要发送的数据
   * ack_num: 指定回复消息序列号
   */
  sendToEdgerOS(type_flags, buf, ack_num) {
    const dataBuf = buf || Buffer.from([])
    let msgHeadersHex = ''
    msgHeadersHex += '15' //magic_ver
    msgHeadersHex += ('00' + type_flags.toString(16)).slice(-2) //type_flags

    if (!ack_num) {
      msgHeadersHex += ('0000' + (this.ackNum++).toString(16)).slice(-4)// ack number
    } else {
      // 回复消息需要用相同ack_num
      msgHeadersHex += ('0000' + (ack_num).toString(16)).slice(-4)// ack number
    }
    msgHeadersHex += this.uid
    msgHeadersHex += '00' // security
    msgHeadersHex += '00' // reserved
    msgHeadersHex += ('0000' + (dataBuf.length).toString(16)).slice(-4) //data length

    let msgHeadersBuf = Buffer.from(msgHeadersHex, 'hex')
    let msgBuffer = Buffer.concat([msgHeadersBuf, dataBuf])

    if (msgBuffer.length < 1460) {
      this.server.send(msgBuffer, 680, this.edgerosIP, (err) => {
        if (err) {
          console.error('Msg to edgeros fail:', err)
        } else {
          // console.log('Msg to edgeros success.',)
        }
      })
    } else {
      console.error('EdgerOS send Msg fail:数据长度超过最大限制')
    }
  }

  /**
   * 创建sddc connector
   * @port: 端口
   */
  createConnector(port) {
    if (!this.connecter) {
      this.connecter = net.createConnection({ host: '192.168.128.1', port: port })
      this.connecter.on('connect', () => {
        // console.log('sddc connector 连接完成')
      })
      this.connecter.on('error', (err) => {
        console.log('sddc connecter error', err)
      })
      this.connecter.on('close', () => {
        this.connecter.destroy()
        this.connecter = null
      })
    } else {
      // console.log("conneter 已创建")
    }
    return this.connecter
  }

  /**
   * 通过connecter 发送消息
   * @param {*} str 
   */
  connecterSend(str) {
    let buf = Buffer.from(str)
    if (this.connecter) {
      this.connecter.write(buf)
    } else {
      console.log('connecter 未链接')
    }
  }
}

// port 680
module.exports = function (config) {
  let sddcClient = new SddcInstance(config)
  return sddcClient
}


